package cinema;

import java.io.Serializable;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Map;
import java.util.Scanner;
import cinema.Movie.Detail;

/**
 * @authors Matt Couzens & Amanda Kosinski
 * @version 29 April 2012
 */

public class BookingSystem implements Serializable {

	private String name;
	private BookingSystem sys;
	private Person loggedInUser;
	private Person mgr;
	private Map<String, Customer> customers; // key = username
	private ArrayList<Movie> movies; // No unique key for movies, as titles can recur (e.g 'Crash')
	private Map<Integer, Screen> screens; // key = screenNumber
	private ArrayList<Showing> showings;
	private Map<Integer, Booking> bookings;
	private int bookingRef;
	private static Scanner sc;

	//=================================================
	/*
	 * Constructors
	 */
	public BookingSystem() {
		this("default");
	}

	public BookingSystem(String s) {
		name = s;
		customers = new HashMap<String, Customer>();
		movies = new ArrayList<Movie>();
		screens = new HashMap<Integer, Screen>();
		showings = new ArrayList<Showing>();
		bookings = new HashMap<Integer, Booking>();
	}

	//=================================================
	// Login and main menu methods
	//=================================================

	public static void main(String[] args){
		new BookingSystem().run();
	}

	void run() {
		if (!Utility.fileExists()) {
			PreLoad.createBookingSystem();
		}
		sys = Utility.initialise();
		loggedInUser = null;
		Menu.mainMenu(sys);
		goodbye();
	}

	// Exit message
	void goodbye(){
		loggedInUser = null;
		System.out.println("\nThanks for visiting " + sys + ". Please come again soon.");
		Utility.save(sys);
	}
	//Username and password login 
	boolean login() {
		String uname, pw;
		int attempts;
		for (attempts = 3; attempts >= 0; attempts--) { //allows 3 attempts 
			Scanner sc = new Scanner(System.in);
			System.out.print("Username:\t");
			uname = sc.nextLine();
			System.out.print("Password:\t");
			pw = sc.nextLine();
			if(findUsername(uname)){
				if(mgr.getPassword().equals(pw)){ //manager login
					loggedInUser = mgr;
					return true;
				}
				else if (customers.get(uname).getPassword().equals(pw)){ //customer login
					loggedInUser = customers.get(uname);
					return true;
				}
			}
			if (attempts > 0) {
				String s = "";
				if (attempts > 1) {
					s = "s";
				}
				System.out.println("Invalid username or password, please try again (" + attempts + " more attempt" + s + ")");
			}
		}
		return false;
	}

	void logout(){
		loggedInUser = null;
	}

	//=================================================
	// CRUD Persons (including Customers)
	//=================================================

	/*
	 * Allows a user to create a username and password
	 */
	boolean newCustomer() {
		sc = new Scanner(System.in);
		String username, password = null;
		boolean unique = false;
		do {
			System.out.print("Choose a username:\t");
			username = sc.nextLine();
			if (!findUsername(username)){ //tests for unique username
				System.out.print("Choose a password:\t");
				password = sc.nextLine();
				unique = true;
			}
			else System.out.println("Username '" + username + "' is already in use, please enter another username.");
		} while (!unique); //If customer has created a unique account program asks for further details 
		String title;
		System.out.print("Enter your title (Mrs/Mr/Miss/Dr/Prof. etc):\t");
		title = sc.nextLine();
		String firstname;
		System.out.print("Enter your first name:\t");
		firstname = sc.nextLine();
		String surname;
		System.out.print("Enter your surname:\t");
		surname = sc.nextLine();
		String address;
		System.out.print("Enter your address (separated by commas and including postcode) :\t");
		address = sc.nextLine();
		String paymentType;
		System.out.print("Enter your payment type (Visa or Mastercard):\t");
		paymentType = sc.nextLine();
		Customer c = new Customer(username, password, title, firstname, surname, address, paymentType);
		addCustomer(c);
		System.out.println("Your account has been successfully created");
		Utility.save(sys);
		return true;
	}

	/*
	 * Helper method to test for uniqueness of new usernames
	 */
	private boolean findUsername(String uname){
		if(mgr.getUsername().equals(uname) || customers.containsKey(uname)){
			return true;
		}
		else return false;
	}

	/**
	 * @param uname
	 * @param pw
	 */
	void addManager(String uname, String pw){
		mgr = new Person(uname, pw, Person.Role.Manager);
	}

	/**
	 * @param uname
	 * @param pw
	 */
	void addCustomer(Customer customer){
		customers.put(customer.getUsername(), customer);
	}

	/**
	 * @return customers
	 */
	Map<String, Customer> getCustomers(){
		return customers;
	}

	/**
	 * @return the loggedInUser
	 */
	public Person getLoggedInUser() {
		return loggedInUser;
	}

	/**
	 * @return the mgr
	 */
	public Person getMgr() {
		return mgr;
	}

	/**
	 * @return name
	 */
	@Override
	public String toString() {
		return name;
	}

	//=================================================
	// CRUD movies
	//=================================================


	// Adds a movie
	public boolean addMovie(Movie mv){
		boolean added = true;
		if (movies.contains(mv)){
			return !added;
		}
		else{
			movies.add(mv);
			return added;
		}
	}

	/**
	 * @param title
	 * @param genre
	 * @param director
	 * @param length
	 * @param cast
	 * @param releaseDate
	 * @param certificate
	 * @param language
	 * @param synopsis
	 * @param trailer
	 */
	public void addMovie(
			String title, 
			String genre, 
			String director, 
			String length, 
			String cast, 
			String releaseDate, 
			String certificate, 
			String language, 
			String synopsis, 
			String trailer) {
		Movie mv = new Movie(
				title, 
				genre, 
				director, 
				length, 
				cast, 
				releaseDate, 
				certificate, 
				language, 
				synopsis, 
				trailer);
		movies.add(mv);
	}

	ArrayList<Movie> getMovies(){
		return movies;
	}

	/**
	 * Returns a movie based on a search
	 * 
	 * @param field the field to be searched on
	 * @param str the string to be searched for
	 */
	Movie getMovieByTitle(String str){
		for (Movie m : movies){
			if(m.getMovieDetails(Detail.Title).equals(str)){
				return m;
			}
		}
		return null;
	}
	//deletes a movie
	void deleteMovie(Movie mv){
		movies.remove(mv);
	}

	//=================================================
	// CRUD showings
	//=================================================

	//adds a showing
	void addShowing(Showing sh){
		showings.add(sh);
	}

	void addShowing(Movie movie, int screen, int year, int month, int date, int hr, int min){
		Showing sh = new Showing(movie, getScreen(screen), year - 1900, month - 1, date, hr, min);
		showings.add(sh);
	}

	ArrayList<Showing> getShowings(){
		return showings;
	}

	boolean updateShowing(){
		return true;
	}

	void deleteShowing(Showing sh){
		showings.remove(sh);
	}

	//=================================================
	// CRUD bookings
	//=================================================

	boolean addBooking(Booking bk){
		bookings.put(bookingRef, bk);
		bookingRef++;
		return true;
	}

	Booking getBooking(int bkref){
		return bookings.get(bkref);
	}

	Map<Integer, Booking> getBookings(){
		return bookings;
	}

	boolean updateBooking(Booking bk){
		return true;
	}

	void deleteBooking(int bkref){
		bookings.remove(bkref);
	}

	int getBookingRef(){
		return bookingRef;
	}

	//=================================================
	// CRUD screens
	//=================================================

	public void addScreen(int screenNumber, int capacity, String layout) {
		Screen scrn = new Screen(screenNumber, capacity, layout);
		screens.put(screenNumber, scrn);
	}

	Screen getScreen(int screenNumber){
		Integer key = screenNumber;
		return screens.get(key);
	}

	Map<Integer, Screen> getScreens(){
		return screens;
	}


	void setScreen(){
		// TODO
	}

	void deleteScreen(){
		// TODO
	}
}